package com.mlc.utils.commom;


import org.springframework.util.StringUtils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

/**
 * ByteUtil
 * byte数组处理工具
 *
 * @author FireWang
 * @version v1.0
 * @date 2021年6月24日
 */
public class ByteUtil {

    /**
     * byte数组转String
     *
     * @param byteArray
     * @param addSpace
     * @return 转化后的String
     */
    public static String byteToHex(byte[] byteArray, boolean addSpace) {
        if (byteArray == null || byteArray.length == 0)
            return null;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < byteArray.length; i++) {
            String hex = Integer.toHexString(byteArray[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
            if (addSpace)
                sb.append(" ");
        }
        return sb.toString().trim();
    }

    /**
     * String转byte数组
     *
     * @param hexString 不带空格的HexString
     * @return 转化后的byte[]
     */
    public static byte[] hexToByte(String hexString) {
        if (StringUtils.isEmpty(hexString)) {
            return null;
        }
        hexString = hexString.toLowerCase();
        final byte[] byteArray = new byte[hexString.length() >> 1];
        int index = 0;
        for (int i = 0; i < hexString.length(); i++) {
            if (index > hexString.length() - 1) {
                return byteArray;
            }
            byte highDit = (byte) (Character.digit(hexString.charAt(index), 16) & 0xFF);
            byte lowDit = (byte) (Character.digit(hexString.charAt(index + 1), 16) & 0xFF);
            byteArray[i] = (byte) (highDit << 4 | lowDit);
            index += 2;
        }
        return byteArray;
    }

    /**
     * 合并byte[]数组 （不改变原数组）
     *
     * @param headByte 并在在头部的数组
     * @param endByte  并在在尾部的数组
     * @return 合并后的数组
     */
    public static byte[] byteMerger(byte[] headByte, byte[] endByte) {
        byte[] target = new byte[headByte.length + endByte.length];
        System.arraycopy(headByte, 0, target, 0, headByte.length);
        System.arraycopy(endByte, 0, target, headByte.length, endByte.length);
        return target;
    }

    /**
     * 截取byte数组   不改变原数组
     *
     * @param dataByte 原数组
     * @param start    偏差值（索引），初始位置
     * @param length   长度
     * @return 截取后的数组
     */
    public static byte[] subByte(byte[] dataByte, int start, int length) {
        byte[] target = new byte[length];
        System.arraycopy(dataByte, start, target, 0, length);
        return target;
    }

    /**
     * 将byte数组分段向后异或   不足分段长度的则末尾补0
     *
     * @param dataByte 原数组
     * @param xorLen   分段长度
     * @param endByte  最后参与异或的数组
     * @return 异或后的数组
     */
    public static byte[] xorByte(int xorLen, byte[] dataByte, byte[] endByte) {
        byte[] target = new byte[xorLen];
        //endByte长度必须为length
        if (xorLen != endByte.length) {
            return null;
        }

        for (int i = 0; i < dataByte.length; i += xorLen) {
            byte[] tmp = new byte[xorLen];

            if (i + xorLen < dataByte.length) {
                System.arraycopy(dataByte, i, tmp, 0, xorLen);
            } else {
                System.arraycopy(dataByte, i, tmp, 0, dataByte.length - i);
            }

            for (int j = 0; j < xorLen; j++) {
                target[j] ^= tmp[j];
            }
        }

        for (int i = 0; i < xorLen; i++) {
            target[i] ^= endByte[i];
        }

        return target;
    }

    /**
     * 对byte数组进行AES加密
     *
     * @param dataByte 需要加密的数据
     * @param aesKey   16位秘钥数组
     * @return
     */
    public static byte[] byteForAes(byte[] dataByte, byte[] aesKey) {
        if (dataByte == null || aesKey == null || aesKey.length != 16) {
            return null;
        }
        try {
            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
            // 创建密码
            Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
            // 初始化
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            //得到加密的校验码
            byte[] target = cipher.doFinal(dataByte);

            return target;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将6字节的数组换转化为时间格式String
     *
     * @param timeByte
     * @return
     */
    public static String byteToDate(byte[] timeByte) {
        if (timeByte == null || timeByte.length != 6) {
            return null;
        }
        String year = "20" + autoDate(Integer.parseInt(Integer.toHexString(timeByte[0] & 0xFF), 16));
        String month = autoDate(Integer.parseInt(Integer.toHexString(timeByte[1] & 0xFF), 16));
        String day = autoDate(Integer.parseInt(Integer.toHexString(timeByte[2] & 0xFF), 16));
        String hour = autoDate(Integer.parseInt(Integer.toHexString(timeByte[3] & 0xFF), 16));
        String minite = autoDate(Integer.parseInt(Integer.toHexString(timeByte[4] & 0xFF), 16));
        String scound = autoDate(Integer.parseInt(Integer.toHexString(timeByte[5] & 0xFF), 16));

        return year + "-" + month + "-" + day + " " + hour + ":" + minite + ":" + scound;
    }

    /**
     * 时间格式前面补0
     *
     * @param date
     * @return
     */
    private static String autoDate(int date) {
        if (date < 10) {
            return "0" + date;
        }
        return date + "";
    }

    /**
     * 将byte数组顺序倒置
     *
     * @param dataByte
     * @return
     */
    public static byte[] byteToTurn(byte[] dataByte) {
        if (dataByte == null || dataByte.length == 0) {
            return null;
        }
        //反转数组
        byte[] turnByte = new byte[dataByte.length];
        for (int i = 0; i < dataByte.length; i++) {
            turnByte[i] = dataByte[dataByte.length - 1 - i];
        }

        return turnByte;
    }

    /**
     * 将数组转化为10进制数字，高位在后
     *
     * @param dataByte
     * @return
     */
    public static Integer byteTurnToInt(byte[] dataByte) {
        if (dataByte == null || dataByte.length == 0) {
            return null;
        }
        //先倒置数组
        byte[] turnByte = byteToTurn(dataByte);

        //输出结果
        return Integer.valueOf(byteToHex(turnByte, false), 16);
    }

    /**
     * 将byte数组顺序倒置后转化为hex
     *
     * @param dataByte
     * @return
     */
    public static String byteTurnToHex(byte[] dataByte) {
        if (dataByte == null || dataByte.length == 0) {
            return null;
        }
        //先倒置数组
        byte[] turnByte = new byte[dataByte.length];

        //再转为hex
        return byteToHex(turnByte, false);
    }

    /**
     * 将hex转化为byte数组后，顺序倒置
     *
     * @param data
     * @return
     */
    public static byte[] hexTurnToByte(String data) {
        if (data == null) {
            return null;
        }
        //获取原数组
        byte[] dataByte = hexToByte(data);
        //反转数组
        byte[] turnByte = byteToTurn(dataByte);

        return turnByte;
    }

    /**
     * 将标准mac格式地址转化为byte数组去加密
     * <p>
     * 注：标准mac格式为 AA:A2:DF:D0:29:D5
     *
     * @param mac
     * @return
     */
    public static byte[] macToByte(String mac) {
        if (mac == null) {
            return null;
        }
        String macArr[] = mac.split(":");
        String realmac = "";
        for (int i = macArr.length - 1; i >= 0; i--) {
            realmac += macArr[i];
        }

        return hexToByte(realmac);
    }

    /**
     * 将hex转码的mac地址转化为标准mac格式
     * <p>
     * 注：hex码mac地址每个字节与实际顺序相反 例：D529D0DFA2AA
     *
     * @param mac
     * @return 标准mac格式为：AA:A2:DF:D0:29:D5
     */
    public static String macToString(String mac) {
        if (mac == null) {
            return null;
        }
        byte[] macByte = hexToByte(mac);
        String realmac = "";
        for (int i = macByte.length - 1; i >= 0; i--) {
            realmac += byteToHex(subByte(macByte, i, 1), false) + ":";
        }

        return realmac.substring(0, realmac.length() - 1);
    }

    public static void main(String[] args) {
        /**
         * 数据构成(data)：[2字节同步头]+[2字节数据长度]+[1字节命令]+[2字节总记录数]+[2字节当前记录数]+[业务数据]+[6字节时间戳]+[16字节认证码]
         * 参与加密的明文数据：[1字节命令]+[2字节总记录数]+[2字节当前记录数]+[业务数据]+[6字节时间戳]
         *
         * 充电记录（32字节:其中蓝牙MAC,6字节;充电ID,4字节;手机AppID,8字节;充电开始时间,6字节;充电时间,3字节;消耗电能,2字节;充电插座,1字节;充电结果,1字节;保留字段,1字节）
         * 终端状态（32字节:其中蓝牙MAC,6字节;充电ID,固定为0x00000000,4字节；记录保存时间,6字节;插座1状态,1字节;插座2状态,1字节;保留字段,14字节）
         */
        //测试HexString数据
        /*String hexStr1 = "55 AA 4B 00 ";
        String hexStr2 = "90 03 00 02 00";
        String data1 = "D529D0DFA2AA 01000000 0000000000000000 15060B0D1E01 080700 2000 01 03 0D";
        String data2 = "D529D0DFA2AA 00000000 00 01 7AEB01B175490000000000000000000000000000";
        String date = "15060B0D1E01 ";
        String code = "A5BA334CE34DCFEBD52E8E8DDACA84D7";

        String hexString = hexStr2 + data1 + data2 + date;
        hexString = hexString.replace(" ", "");*/

        //mac、随机数、key
        /*byte[] macByte = macToByte("D5:29:D0:DF:A2:AA");
        byte[] randomByte = hexToByte("00000000000000000000");
        byte[] keyByte = hexToByte("6452724f4567463953424f446a326675");

        *//** ---------------- 数据合法性检验验 - 16位认证码验证 - 用秘钥算出认证码与其对比 ----------------- *//*
        //先将参与加密的明文数据（[1字节命令]+[2字节总记录数]+[2字节当前记录数]+[业务数据]+[6字节时间戳]）与16位明文（6位mac+10位随机数）异或
        byte[] tempByte = ByteUtil.xorByte(16, hexToByte(hexString), byteMerger(macByte, randomByte));
        //用通信mac对应的秘钥对其进行加密
        byte[] nowCode = ByteUtil.byteForAes(tempByte, keyByte);

        System.out.println("认证码：" + byteToHex(nowCode, false));*/

        String data = "55aa4b02901200120048e707d0a2aa00000000150a120c1c32000000000000000000000000000000008c23dcdfa2aa00000000150a120d172c02004508000000000000000000000000811cfcdfa2aa00000000150a120d18090200460800000000000000000000000058f42fd0a2aa00000000150a120c191900030000000000001808000005000000032618d0a2aa00000000150a120c1a0800030000000000001908000004000000a0205cd0a2aa00000000150a120c1a340000000000000000000000000000000062f67bd0a2aa00000000150a120c300b0000000000000000000000000000000028eddfdfa2aa00000000150a120c1b330000000000000000000000000000000093fa57d0a2aa00000000150a120c1b3a01000000000000000000000000000000bf1874dfa2aa00000000150a120c300c0100000000000000000000000000000049f57bdfa2aa00000000150a0e02351c000000000000000000000000000000007d0bd8dfa2aa00000000150a12063002010000000000000000000000000000004d0980d0a2aa00000000150a0d002c020000000000000000000000000000000048d8abdfa2aa00000000150a0e0c32340200130500000000000000000000000031da0f9073aa00000000150a1017340c00010000000000000000000000000000bbe14fd0a2aa00000000150a0e0c351f00010000000000000000000000000000f0fa83d0a2aa00000000150a1115040a010000000000000000000000000000008bdee79f73aa00000000150a0d0f173200000000000000000000000000000000150a120d1818b3d793d7876e68a31dc5f47fdb7e6896";

        //换为byte
        byte[] wholeData = hexToByte(data);

        //截取业务数据：作为验证成功的返回数据
        byte[] transData = subByte(wholeData, 9, wholeData.length - 9 - 6 - 16);

        //循环打印每条数据
        for (int i = 0; i < transData.length; i += 32) {
            byte[] tempData = ByteUtil.subByte(transData, i, 32);
            //根据是否有充电ID来判断记录类型
            byte[] chargeId = ByteUtil.subByte(tempData, 6, 4);
            String noChargeId = "00000000";
            String chargId = ByteUtil.byteToHex(chargeId, false);
            if (noChargeId.equals(chargId)) {
                //没有充电ID则为：终端状态
                System.out.println("终端状态" + (i / 32 + 1));
                System.out.println("mac：" + macToString(byteToHex(subByte(tempData, 0, 6), false)));
                System.out.println("chargeId：" + byteToHex(subByte(tempData, 6, 4), false));
                System.out.println("statusTime：" + byteToDate(subByte(tempData, 10, 6)));
                System.out.println("status1：" + subByte(tempData, 16, 1)[0]);
                System.out.println("status2：" + subByte(tempData, 17, 1)[0]);
                System.out.println("chargeId1：" + byteTurnToInt(ByteUtil.subByte(tempData, 18, 4)));
                System.out.println("takes1：" + byteTurnToInt(ByteUtil.subByte(tempData, 22, 2)));
                System.out.println("chargeId2：" + byteTurnToInt(ByteUtil.subByte(tempData, 24, 4)));
                System.out.println("takes2：" + byteTurnToInt(ByteUtil.subByte(tempData, 28, 2)));
                System.out.println("keep：" + byteToHex(subByte(tempData, 30, 2), false));
                System.out.println("\n");
            } else {
                //有充电ID则为：充电记录
                System.out.println("充电记录" + (i / 32 + 1));
                System.out.println("mac：" + macToString(byteToHex(subByte(tempData, 0, 6), false)));
                System.out.println("chargeId：" + byteTurnToInt(subByte(tempData, 6, 4)));
                System.out.println("appId：" + byteToHex(subByte(tempData, 10, 8), false));
                System.out.println("startTime：" + byteToDate(subByte(tempData, 18, 6)));
                System.out.println("chargeTime：" + byteTurnToInt(subByte(tempData, 24, 2)));
                System.out.println("chargeType：" + byteTurnToInt(subByte(tempData, 26, 1)));
                System.out.println("kws：" + byteTurnToInt(subByte(tempData, 27, 2)));
                System.out.println("sokect：" + byteTurnToInt(subByte(tempData, 29, 1)));
                System.out.println("status：" + byteTurnToInt(subByte(tempData, 30, 1)));
                System.out.println("keep：" + byteToHex(subByte(tempData, 31, 1), false));
                System.out.println("\n");
            }

        }
    }
}
